using System;
using System.Linq;
namespace Net35.Xrm.Sdk.Client
{
	public abstract class ServiceProxy<TService> : IDisposable where TService : class
	{
		private ServiceChannel<TService> _serviceChannel;
		private System.ServiceModel.ChannelFactory<TService> _channelFactory;
		private TimeSpan _timeout = ServiceDefaults.DefaultTimeout;
		private bool _autoCloseChannel;
		public event EventHandler<ChannelFaultedEventArgs> FactoryFaulted;
		public event EventHandler<ChannelEventArgs> FactoryOpened;
		public event EventHandler<ChannelEventArgs> FactoryClosed;
		public event EventHandler<EndpointSwitchEventArgs> EndpointSwitched
		{
			add
			{
				this.EndpointSwitch.EndpointSwitched += value;
			}
			remove
			{
				this.EndpointSwitch.EndpointSwitched -= value;
			}
		}
		public event EventHandler<EndpointSwitchEventArgs> EndpointSwitchRequired
		{
			add
			{
				this.EndpointSwitch.EndpointSwitchRequired += value;
			}
			remove
			{
				this.EndpointSwitch.EndpointSwitchRequired -= value;
			}
		}
		public IServiceConfiguration<TService> ServiceConfiguration
		{
			get;
			private set;
		}
		public IServiceManagement<TService> ServiceManagement
		{
			get
			{
				return this.ServiceConfiguration as IServiceManagement<TService>;
			}
		}
		public IEndpointSwitch EndpointSwitch
		{
			get
			{
				return this.ServiceConfiguration as IEndpointSwitch;
			}
		}
		public System.ServiceModel.Description.ClientCredentials ClientCredentials
		{
			get;
			private set;
		}
		public string UserPrincipalName
		{
			get;
			set;
		}
		public SecurityTokenResponse SecurityTokenResponse
		{
			get;
			protected set;
		}
		public SecurityTokenResponse HomeRealmSecurityTokenResponse
		{
			get;
			protected set;
		}
		public System.Uri HomeRealmUri
		{
			get;
			private set;
		}
		public System.ServiceModel.Description.ClientCredentials DeviceCredentials
		{
			get;
			private set;
		}
		public ServiceChannel<TService> ServiceChannel
		{
			get
			{
				this.ValidateAuthentication();
				return this._serviceChannel;
			}
		}
		public TimeSpan Timeout
		{
			get
			{
				return this._timeout;
			}
			set
			{
				this._timeout = value;
				this.RefreshChannelManagers();
			}
		}
		public bool IsAuthenticated
		{
			get;
			private set;
		}
		internal bool AutoCloseChannel
		{
			get
			{
				return this._autoCloseChannel;
			}
			set
			{
				this._autoCloseChannel = value;
			}
		}
		internal string AppliesTo
		{
			get;
			set;
		}
		protected System.ServiceModel.ChannelFactory<TService> ChannelFactory
		{
			get
			{
				if (this.IsChannelFactoryInvalid())
				{
					if (this.SecurityTokenResponse == null)
					{
						this._channelFactory = this.ServiceConfiguration.CreateChannelFactory(this.ClientCredentials);
					}
					else
					{
						this._channelFactory = this.ServiceConfiguration.CreateChannelFactory(ClientAuthenticationType.SecurityToken);
					}
					ServiceProxy<TService>.ConfigureEndpoint(this._channelFactory.Endpoint, this);
				}
				return this._channelFactory;
			}
		}
		public bool EndpointAutoSwitchEnabled
		{
			get
			{
				return this.EndpointSwitch.EndpointAutoSwitchEnabled;
			}
			set
			{
				this.EndpointSwitch.EndpointAutoSwitchEnabled = value;
			}
		}
		internal ServiceProxy()
		{
		}
		protected ServiceProxy(IServiceManagement<TService> serviceManagement, SecurityTokenResponse securityTokenResponse) : this(serviceManagement as IServiceConfiguration<TService>, securityTokenResponse)
		{
		}
		protected ServiceProxy(IServiceConfiguration<TService> serviceConfiguration, SecurityTokenResponse securityTokenResponse)
		{
			ClientExceptionHelper.ThrowIfNull(serviceConfiguration, "serviceConfiguration");
			ClientExceptionHelper.ThrowIfNull(serviceConfiguration.CurrentServiceEndpoint, "serviceConfiguration.CurrentServiceEndpoint");
			ClientExceptionHelper.ThrowIfNull(securityTokenResponse, "securityTokenResponse");
			ClientExceptionHelper.ThrowIfNull(securityTokenResponse.Token, "securityTokenResponse.Token");
			this.ServiceConfiguration = serviceConfiguration;
			this.SecurityTokenResponse = securityTokenResponse;
			this.IsAuthenticated = true;
			this.SetDefaultEndpointSwitchBehavior();
		}
		protected ServiceProxy(IServiceManagement<TService> serviceManagement, System.ServiceModel.Description.ClientCredentials clientCredentials) : this(serviceManagement as IServiceConfiguration<TService>, clientCredentials)
		{
		}
		protected ServiceProxy(IServiceConfiguration<TService> serviceConfiguration, System.ServiceModel.Description.ClientCredentials clientCredentials)
		{
			ClientExceptionHelper.ThrowIfNull(serviceConfiguration, "serviceConfiguration");
			ClientExceptionHelper.ThrowIfNull(serviceConfiguration.CurrentServiceEndpoint, "serviceConfiguration.CurrentServiceEndpoint");
			this.ServiceConfiguration = serviceConfiguration;
			this.SetClientCredentials(clientCredentials);
			this.IsAuthenticated = true;
			this.SetDefaultEndpointSwitchBehavior();
		}
		protected ServiceProxy(System.Uri uri, System.Uri homeRealmUri, System.ServiceModel.Description.ClientCredentials clientCredentials, System.ServiceModel.Description.ClientCredentials deviceCredentials)
		{
			ClientExceptionHelper.ThrowIfNull(uri, "uri");
			this.IsAuthenticated = false;
			this.ServiceConfiguration = ServiceConfigurationFactory.CreateConfiguration<TService>(uri);
			this.SetClientCredentials(clientCredentials);
			this.HomeRealmUri = homeRealmUri;
			this.DeviceCredentials = deviceCredentials;
			this.SetDefaultEndpointSwitchBehavior();
		}
		public void Authenticate()
		{
			if (this._serviceChannel != null)
			{
				this._serviceChannel.Close();
				this._serviceChannel.Dispose();
				this._serviceChannel = null;
			}
			if (this._channelFactory != null)
			{
				this.RemoveChannelFactoryEvents();
				this._channelFactory.Close(true);
				this._channelFactory = null;
			}
			this.AuthenticateCore();
		}
		public SecurityTokenResponse AuthenticateCrossRealm()
		{
			return this.AuthenticateCrossRealmCore();
		}
		public SecurityTokenResponse AuthenticateDevice()
		{
			return this.AuthenticateDeviceCore();
		}
		protected virtual SecurityTokenResponse AuthenticateCrossRealmCore()
		{
			ClientExceptionHelper.ThrowIfNull(this.ServiceConfiguration, "ServiceConfiguration");
			ClientExceptionHelper.ThrowIfNull(this.HomeRealmUri, "HomeRealmUri");
			if (this.AppliesTo == null)
			{
				ClientExceptionHelper.ThrowIfNull(this.ServiceConfiguration.PolicyConfiguration, "ServiceConfiguration.PolicyConfiguration");
				ClientExceptionHelper.ThrowIfNullOrEmpty(this.ServiceConfiguration.PolicyConfiguration.SecureTokenServiceIdentifier, "ServiceConfiguration.PolicyConfiguration.SecureTokenServiceIdentifier");
				this.AppliesTo = this.ServiceConfiguration.PolicyConfiguration.SecureTokenServiceIdentifier;
			}
			return this.ServiceConfiguration.AuthenticateCrossRealm(this.ClientCredentials, this.AppliesTo, this.HomeRealmUri);
		}
		protected bool? ShouldRetry(System.ServiceModel.Security.MessageSecurityException messageSecurityException, bool? retry)
		{
			if (!retry.HasValue)
			{
				System.ServiceModel.FaultException ex = messageSecurityException.InnerException as System.ServiceModel.FaultException;
				if (ex != null && ex.Code.IsSenderFault && ex.Code.SubCode.Name == "BadContextToken")
				{
					return new bool?(true);
				}
			}
			return new bool?(false);
		}
		protected virtual void ValidateAuthentication()
		{
			if (!this.IsAuthenticated)
			{
				this.Authenticate();
			}
			if (this._serviceChannel == null || this._serviceChannel.IsChannelInvalid)
			{
				this.CreateNewServiceChannel();
			}
		}
		protected virtual SecurityTokenResponse AuthenticateDeviceCore()
		{
			if (this.ServiceConfiguration.AuthenticationType == AuthenticationProviderType.LiveId)
			{
				ClientExceptionHelper.ThrowIfNull(this.DeviceCredentials, "DeviceCredentials");
				return this.ServiceConfiguration.AuthenticateDevice(this.DeviceCredentials);
			}
			return null;
		}
		protected virtual void AuthenticateCore()
		{
			ClientExceptionHelper.ThrowIfNull(this.ServiceConfiguration, "ServiceConfiguration");
			if (this.ServiceConfiguration.AuthenticationType == AuthenticationProviderType.ActiveDirectory)
			{
				this.IsAuthenticated = true;
				return;
			}
			if (this.ClientCredentials != null)
			{
				SecurityTokenResponse securityTokenResponse = null;
				switch (this.ServiceConfiguration.AuthenticationType)
				{
				case AuthenticationProviderType.Federation:
					securityTokenResponse = this.AuthenticateClaims();
					break;
				case AuthenticationProviderType.LiveId:
					securityTokenResponse = this.AuthenticateWithLiveId();
					break;
				case AuthenticationProviderType.OnlineFederation:
					if (this.ServiceConfiguration.ShouldAuthenticateWithLiveId(this.ClientCredentials))
					{
						AuthenticationCredentials authenticationCredentials = new AuthenticationCredentials();
						authenticationCredentials.ClientCredentials = this.ClientCredentials;
						if (this.DeviceCredentials != null)
						{
							authenticationCredentials.SupportingCredentials = new AuthenticationCredentials();
							authenticationCredentials.SupportingCredentials.ClientCredentials = this.DeviceCredentials;
						}
						AuthenticationCredentials authenticationCredentials2 = this.ServiceManagement.Authenticate(authenticationCredentials);
						securityTokenResponse = authenticationCredentials2.SecurityTokenResponse;
					}
					else
					{
						securityTokenResponse = this.AuthenticateOnlineFederation();
					}
					break;
				}
				ClientExceptionHelper.Assert(securityTokenResponse != null && securityTokenResponse.Token != null, "The user authentication failed!");
				this.SecurityTokenResponse = securityTokenResponse;
				this.IsAuthenticated = true;
			}
		}
		private SecurityTokenResponse AuthenticateOnlineFederation()
		{
			OnlinePolicyConfiguration onlinePolicyConfiguration = this.ServiceConfiguration.PolicyConfiguration as OnlinePolicyConfiguration;
			ClientExceptionHelper.ThrowIfNull(onlinePolicyConfiguration, "onlinePolicy");
			OrgIdentityProviderTrustConfiguration orgIdentityProviderTrustConfiguration = onlinePolicyConfiguration.OnlineProviders.Values.OfType<OrgIdentityProviderTrustConfiguration>().FirstOrDefault<OrgIdentityProviderTrustConfiguration>();
			ClientExceptionHelper.ThrowIfNull(orgIdentityProviderTrustConfiguration, "liveTrustConfig");
			bool flag = true;
			if (this.HomeRealmUri == null)
			{
				string text = this.UserPrincipalName;
				if (string.IsNullOrEmpty(text))
				{
					if (string.IsNullOrEmpty(this.ClientCredentials.Windows.ClientCredential.UserName))
					{
						ClientExceptionHelper.ThrowIfNullOrEmpty(this.ClientCredentials.UserName.UserName, "ClientCredentials.UserName.UserName");
						text = this.ClientCredentials.UserName.UserName;
					}
					else
					{
						text = this.ClientCredentials.Windows.ClientCredential.UserName;
					}
				}
				IdentityProvider identityProvider = this.ServiceConfiguration.GetIdentityProvider(text);
				ClientExceptionHelper.ThrowIfNull(identityProvider, "identityProvider");
				this.HomeRealmUri = identityProvider.ServiceUrl;
				flag = (identityProvider.IdentityProviderType == IdentityProviderType.OrgId);
				if (flag)
				{
					ClientExceptionHelper.Assert(onlinePolicyConfiguration.OnlineProviders.ContainsKey(this.HomeRealmUri), "Online Identity Provider NOT found!  {0}", new object[]
					{
						this.HomeRealmUri
					});
				}
			}
			if (flag)
			{
				return this.ServiceConfiguration.AuthenticateCrossRealm(this.ClientCredentials, orgIdentityProviderTrustConfiguration.AppliesTo, this.HomeRealmUri);
			}
			this.AppliesTo = orgIdentityProviderTrustConfiguration.Identifier.AbsoluteUri;
			this.HomeRealmSecurityTokenResponse = this.AuthenticateCrossRealm();
			ClientExceptionHelper.Assert(this.HomeRealmSecurityTokenResponse != null && this.HomeRealmSecurityTokenResponse.Token != null, "The user authentication failed!");
			return this.ServiceConfiguration.AuthenticateCrossRealm(this.HomeRealmSecurityTokenResponse.Token, orgIdentityProviderTrustConfiguration.AppliesTo, orgIdentityProviderTrustConfiguration.Endpoint.GetServiceRoot());
		}
		private SecurityTokenResponse AuthenticateClaims()
		{
			if (this.HomeRealmUri != null)
			{
				this.HomeRealmSecurityTokenResponse = this.AuthenticateCrossRealm();
				ClientExceptionHelper.Assert(this.HomeRealmSecurityTokenResponse != null && this.HomeRealmSecurityTokenResponse.Token != null, "The user authentication failed!");
				return this.ServiceConfiguration.Authenticate(this.HomeRealmSecurityTokenResponse.Token);
			}
			return this.ServiceConfiguration.Authenticate(this.ClientCredentials);
		}
		private SecurityTokenResponse AuthenticateWithLiveId()
		{
			SecurityTokenResponse securityTokenResponse = this.AuthenticateDevice();
			ClientExceptionHelper.Assert(securityTokenResponse != null && securityTokenResponse.Token != null, "The device authentication failed!");
			if (securityTokenResponse != null)
			{
				return this.ServiceConfiguration.Authenticate(this.ClientCredentials, securityTokenResponse);
			}
			return null;
		}
		protected virtual void CloseChannel(bool forceClose)
		{
			if ((forceClose || this.AutoCloseChannel) && this._serviceChannel != null)
			{
				this._serviceChannel.Close();
			}
		}
		protected static void SetBindingTimeout(System.ServiceModel.Channels.Binding binding, TimeSpan sendTimeout, TimeSpan openTimeout, TimeSpan closeTimeout)
		{
			ClientExceptionHelper.ThrowIfNull(binding, "binding");
			binding.OpenTimeout = openTimeout;
			binding.CloseTimeout = closeTimeout;
			binding.SendTimeout = sendTimeout;
		}
		internal static void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint endpoint, ServiceProxy<TService> serviceProxy)
		{
			ClientExceptionHelper.ThrowIfNull(endpoint, "endpoint");
			ClientExceptionHelper.ThrowIfNull(serviceProxy, "serviceProxy");
			foreach (System.ServiceModel.Description.OperationDescription current in endpoint.Contract.Operations)
			{
				System.ServiceModel.Description.DataContractSerializerOperationBehavior dataContractSerializerOperationBehavior = current.Behaviors.Find<System.ServiceModel.Description.DataContractSerializerOperationBehavior>();
				if (dataContractSerializerOperationBehavior != null)
				{
					dataContractSerializerOperationBehavior.MaxItemsInObjectGraph = 2147483647;
				}
			}
			XrmBinding xrmBinding = new XrmBinding(endpoint.Binding);
			endpoint.Binding = xrmBinding;
			xrmBinding.MaxReceivedMessageSize = 2147483647L;
			xrmBinding.MaxBufferSize = 2147483647;
			xrmBinding.ReaderQuotas.MaxStringContentLength = 2147483647;
			xrmBinding.ReaderQuotas.MaxArrayLength = 2147483647;
			xrmBinding.ReaderQuotas.MaxBytesPerRead = 2147483647;
			ServiceProxy<TService>.SetBindingTimeout(xrmBinding, serviceProxy.Timeout, serviceProxy.Timeout, serviceProxy.Timeout);
		}
		private void SetClientCredentials(System.ServiceModel.Description.ClientCredentials clientCredentials)
		{
			this.ClientCredentials = (clientCredentials ?? new System.ServiceModel.Description.ClientCredentials());
			if (this.ServiceConfiguration.AuthenticationType == AuthenticationProviderType.ActiveDirectory)
			{
				ServiceMetadataUtility.AdjustUserNameForWindows(this.ClientCredentials);
			}
		}
		private void CreateNewServiceChannel()
		{
			this.ChannelFactory.Faulted += new EventHandler(this.Factory_Faulted);
			this.ChannelFactory.Opened += new EventHandler(this.Factory_Opened);
			this.ChannelFactory.Closed += new EventHandler(this.Factory_Closed);
			this._serviceChannel = ((this.SecurityTokenResponse != null && this.SecurityTokenResponse.Token != null) ? new ServiceFederatedChannel<TService>(this.ChannelFactory, this.SecurityTokenResponse.Token) : new ServiceChannel<TService>(this.ChannelFactory));
			this._serviceChannel.Timeout = this.Timeout;
		}
		private void RefreshChannelManagers()
		{
			if (this._channelFactory != null)
			{
				ServiceProxy<TService>.SetBindingTimeout(this._channelFactory.Endpoint.Binding, this._timeout, this._timeout, this._timeout);
			}
			if (this._serviceChannel != null)
			{
				this._serviceChannel.Timeout = this._timeout;
			}
		}
		private bool IsChannelFactoryInvalid()
		{
			if (this._channelFactory != null)
			{
				System.ServiceModel.CommunicationState state = this._channelFactory.State;
				if (state == System.ServiceModel.CommunicationState.Opened || state == System.ServiceModel.CommunicationState.Created || state == System.ServiceModel.CommunicationState.Opening)
				{
					return false;
				}
			}
			return true;
		}
		private void RemoveChannelFactoryEvents()
		{
			if (this._channelFactory != null)
			{
				this._channelFactory.Faulted -= new EventHandler(this.Factory_Faulted);
				this._channelFactory.Opened -= new EventHandler(this.Factory_Opened);
				this._channelFactory.Closed -= new EventHandler(this.Factory_Closed);
			}
		}
		private void Factory_Faulted(object sender, EventArgs e)
		{
			this.OnFactoryFaulted(new ChannelFaultedEventArgs("The Factory has entered a faulted state.", null));
			if (this._channelFactory != null)
			{
				this.RemoveChannelFactoryEvents();
				this._channelFactory.Close(true);
				this._channelFactory = null;
			}
		}
		protected virtual void OnFactoryFaulted(ChannelFaultedEventArgs args)
		{
			if (this.FactoryFaulted != null)
			{
				this.FactoryFaulted(this, args);
			}
		}
		private void Factory_Closed(object sender, EventArgs e)
		{
			this.OnFactoryClosed(new ChannelEventArgs("The Factory has entered a closed state."));
		}
		protected virtual void OnFactoryClosed(ChannelEventArgs args)
		{
			if (this.FactoryClosed != null)
			{
				this.FactoryClosed(this, args);
			}
		}
		private void Factory_Opened(object sender, EventArgs e)
		{
			this.OnFactoryOpened(new ChannelEventArgs("The Factory has entered an opened state."));
		}
		protected virtual void OnFactoryOpened(ChannelEventArgs args)
		{
			if (this.FactoryOpened != null)
			{
				this.FactoryOpened(this, args);
			}
		}
		protected bool? HandleFailover(BaseServiceFault fault, bool? retry)
		{
			if (fault.ErrorCode == -2147176347 && this.HandleFailover(retry))
			{
				return new bool?(true);
			}
			return new bool?(false);
		}
		protected bool HandleFailover(bool? retry)
		{
			if (!retry.HasValue)
			{
				if (!this.EndpointSwitch.CanSwitch(this.ChannelFactory.Endpoint.Address.Uri))
				{
					this.Dispose(true);
					return true;
				}
				if (this.EndpointSwitch.HandleEndpointSwitch())
				{
					this.Dispose(true);
					return true;
				}
			}
			return false;
		}
		public bool SwitchToAlternateEndpoint()
		{
			if (this.EndpointAutoSwitchEnabled && this.EndpointSwitch.AlternateEndpoint != null)
			{
				this.EndpointSwitch.SwitchEndpoint();
				return true;
			}
			return false;
		}
		private void SetDefaultEndpointSwitchBehavior()
		{
			this.EndpointAutoSwitchEnabled = (this.EndpointSwitch.AlternateEndpoint != null);
		}
		public void Dispose()
		{
			this.Dispose(true);
			GC.SuppressFinalize(this);
		}
		~ServiceProxy()
		{
			this.Dispose(false);
		}
		internal void DisposeFactory(bool disposing)
		{
			if (disposing)
			{
				if (this._channelFactory != null)
				{
					this.RemoveChannelFactoryEvents();
					this._channelFactory.Close(true);
				}
				this._channelFactory = null;
			}
		}
		protected virtual void Dispose(bool disposing)
		{
			if (disposing)
			{
				this.DisposeFactory(disposing);
				if (this._serviceChannel != null)
				{
					this._serviceChannel.Dispose();
				}
				this._serviceChannel = null;
			}
		}
	}
}
